#define F_CPU 12000000UL

#include "main.h"
#include "enc28j60.h"
#include <avr/io.h>
#ifndef ALIBC_OLD
#include <util/delay.h>
#else
#include <avr/delay.h>
#endif

static byte ENC28J60_Bank;
static UINT16 NextPacketPtr;

byte ENC28J60_ReadOp( byte op, byte addr )
{
  ENC28J60_ENABLE();
  SPDR = op | addr;
  loop_until_bit_is_set( SPSR, SPIF );
  SPDR = 0xFF;
  loop_until_bit_is_set( SPSR, SPIF );
  ENC28J60_DISABLE();
  
  return SPDR;
}

void ENC28J60_WriteOp( byte op, byte addr, byte data )
{
  ENC28J60_ENABLE();
  SPDR = op | addr ;
  loop_until_bit_is_set( SPSR, SPIF );
  SPDR = data;
  loop_until_bit_is_set( SPSR, SPIF );
  ENC28J60_DISABLE();
  
  return;
}

void ENC28J60_ReadBuffer( UINT16 len, byte* data )
{
  ENC28J60_ENABLE();
  SPDR = ENC28J60_RBM;
  loop_until_bit_is_set( SPSR, SPIF );
  
  while(len)
  {
    len--;
    SPDR = 0xFF;
    loop_until_bit_is_set( SPSR, SPIF );
    *data = SPDR;
    data++;
  }
  //*data = '\0';
  ENC28J60_DISABLE();
  
  return;
}

void ENC28J60_WriteBuffer(UINT16 len, byte* data )
{
  ENC28J60_ENABLE();
  SPDR = ENC28J60_WBM;
  loop_until_bit_is_set( SPSR, SPIF );
  
  while( len )
  {
    len--;
    SPDR = *data;
    loop_until_bit_is_set( SPSR, SPIF );
    data++;
  }
  ENC28J60_DISABLE();
  
  return;
}

void ENC28J60_SetBank( byte addr )
{
  if( ENC28J60_Bank != (addr & ENC28J60_BANK_MASK ) )	/*change bank if needed*/
  {
    ENC28J60_WriteOp( ENC28J60_BFC, ENC28J60_ECON1, (ECON1_BSEL1 | ECON1_BSEL0 )); /* clear bsel0 bsel1*/
    ENC28J60_WriteOp( ENC28J60_BFS, ENC28J60_ECON1, (ENC28J60_BANK_MASK & addr )>>5); /* select bank */
    
    ENC28J60_Bank = ( addr & ENC28J60_BANK_MASK );
  }
  
  return;
}

byte ENC28J60_Read( byte addr )
{
  ENC28J60_SetBank( addr ); /*set bank*/
  
  return ENC28J60_ReadOp( ENC28J60_RCR, addr ); /* read pointed register */
}

void ENC28J60_Write( byte addr, byte data )
{
  ENC28J60_SetBank( addr ); /*set bank*/
  
  return ENC28J60_WriteOp( ENC28J60_WCR, addr, data ); /* write pointed register */
}

void ENC28J60_PhyWrite( byte addr, UINT16 data )
{
  ENC28J60_Write(ENC28J60_MIREGADR, addr); /* set the PHY register address*/
  ENC28J60_Write(ENC28J60_MIWRL, data); /* write the PHY data */
  ENC28J60_Write(ENC28J60_MIWRH, data>>8);
  while(ENC28J60_Read(ENC28J60_MISTAT) & 0x01)/* wait until the PHY write completes */
  { 
    _delay_us(15);
  }
        
  return;
}

/* ======================================== */
void ENC28J60_Init( byte* macaddr )
{
  ENC28J60_DISABLE();
  
  ENC28J60_WriteOp( ENC28J60_SRC, 0x00, ENC28J60_SRC ); /* soft reset */
  _delay_ms(50);
  
  /* =============== init Rx and Tx pointers ========================*/
  NextPacketPtr = RXSTART_INIT;	
  ENC28J60_Write(ENC28J60_ERXSTL, RXSTART_INIT&0xFF);	
  ENC28J60_Write(ENC28J60_ERXSTH, RXSTART_INIT>>8);
  
  ENC28J60_Write(ENC28J60_ERXRDPTL, RXSTART_INIT&0xFF);
  ENC28J60_Write(ENC28J60_ERXRDPTH, RXSTART_INIT>>8);
  
  ENC28J60_Write(ENC28J60_ERXNDL, RXSTOP_INIT&0xFF);
  ENC28J60_Write(ENC28J60_ERXNDH, RXSTOP_INIT>>8);
  
  ENC28J60_Write(ENC28J60_ETXSTL, TXSTART_INIT&0xFF);
  ENC28J60_Write(ENC28J60_ETXSTH, TXSTART_INIT>>8);
  
  ENC28J60_Write(ENC28J60_ETXNDL, TXSTOP_INIT&0xFF);
  ENC28J60_Write(ENC28J60_ETXNDH, TXSTOP_INIT>>8);
  
  /*============= init recieve filters ===============================*/
  /*accept only broadcast and unicast (MAC) */
  /*06 08 ... ff ff ff ff ff ff -> checksum f7f9*/
  /*binary positions 0011 0000 0011 1111 -> 0x303F*/
  ENC28J60_Write(ENC28J60_ERXFCON, ERXFCON_UCEN|ERXFCON_CRCEN|ERXFCON_PMEN);
  ENC28J60_Write(ENC28J60_EPMM0, 0x3f);
  ENC28J60_Write(ENC28J60_EPMM1, 0x30);
  ENC28J60_Write(ENC28J60_EPMSCL, 0xf9);
  ENC28J60_Write(ENC28J60_EPMSCH, 0xf7);
  
  /*============= MAC =================================================*/
  ENC28J60_Write(ENC28J60_MACON1, MACON1_MARXEN|MACON1_TXPAUS|MACON1_RXPAUS); /* MAC receive */
 /* ENC28J60_Write(ENC28J60_MACON0, 0x00);*/
  ENC28J60_WriteOp(ENC28J60_BFS, ENC28J60_MACON3, MACON3_PADCFG0|MACON3_TXCRCEN|MACON3_FRMLEN);/*automatic padding to 60bytes and CRC operations*/
  ENC28J60_Write(ENC28J60_MAIPGL, 0x12);
  ENC28J60_Write(ENC28J60_MAIPGH, 0x0C);
  ENC28J60_Write(ENC28J60_MABBIPG, 0x12);
  ENC28J60_Write(ENC28J60_MAMXFLL, MAX_FRAMELEN&0xFF); /*max frame sieze*/ 
  ENC28J60_Write(ENC28J60_MAMXFLH, MAX_FRAMELEN>>8);
  
  ENC28J60_Write( ENC28J60_MAADR6, macaddr[0] );
  ENC28J60_Write( ENC28J60_MAADR5, macaddr[1] );
  ENC28J60_Write( ENC28J60_MAADR4, macaddr[2] );
  ENC28J60_Write( ENC28J60_MAADR3, macaddr[3] );
  ENC28J60_Write( ENC28J60_MAADR2, macaddr[4] );
  ENC28J60_Write( ENC28J60_MAADR1, macaddr[5] );
  
  ENC28J60_PhyWrite( ENC28J60_PHCON2, 0x0100 ); /*no loopback*/
  
  ENC28J60_SetBank( ENC28J60_ECON1 );
  ENC28J60_WriteOp( ENC28J60_BFS, ENC28J60_EIE, EIE_INTIE | EIE_PKTIE ); /*enable interrupts*/
  ENC28J60_WriteOp( ENC28J60_BFS, ENC28J60_ECON1, ECON1_RXEN ); /*enable packet recepton*/
  
  return;
}


void ENC28J60_SendPacket( UINT16 len, byte* packet )
{
  /* set pointer to start of Tx buffer*/
  ENC28J60_Write( ENC28J60_EWRPTL, TXSTART_INIT );
  ENC28J60_Write( ENC28J60_EWRPTH, (TXSTART_INIT>>8) );
  /* update TXND */
  ENC28J60_Write( ENC28J60_ETXNDL, TXSTART_INIT+len);
  ENC28J60_Write( ENC28J60_ETXNDH, (TXSTART_INIT+len)>>8 );
  /* write per packet control byte */
  ENC28J60_WriteOp( ENC28J60_WBM, 0, 0x00 );
  /* copy packet into transmit buffer */
  ENC28J60_WriteBuffer( len, packet );
  /* send packet */
  ENC28J60_WriteOp( ENC28J60_BFS, ENC28J60_ECON1, ECON1_TXRTS);
  /* reset transmit logic <B4 silicon errata>*/
  if( ENC28J60_Read( ENC28J60_EIR ) & EIR_TXERIF )
  {
    ENC28J60_WriteOp( ENC28J60_BFC, ENC28J60_ECON1, ECON1_TXRTS );
  }
  
  return;
}

/*returns packet length if successfull else 0*/
UINT16 ENC28J60_ReceivePacket( UINT16 len, byte* packet)
{
  UINT16 rxstat;
  UINT16 _len;
  
  /*check if packet have been received */
  if( ENC28J60_Read(ENC28J60_EPKTCNT) == 0 )
  {
    return(0);
  }
  
  /* set pointers*/
  ENC28J60_Write(ENC28J60_ERDPTL, NextPacketPtr );
  ENC28J60_Write(ENC28J60_ERDPTH, NextPacketPtr>>8 );
  /* read the next packet pointer */
  NextPacketPtr = ENC28J60_ReadOp(ENC28J60_RBM, 0);
  NextPacketPtr |=ENC28J60_ReadOp(ENC28J60_RBM, 0)<<8;
  /* read packet length */
  _len = ENC28J60_ReadOp(ENC28J60_RBM, 0 );
  _len |=ENC28J60_ReadOp(ENC28J60_RBM, 0 )<<8;
  _len -= 4; /* skip CRC */
  /* read the receive status*/
  rxstat = ENC28J60_ReadOp(ENC28J60_RBM, 0);
  rxstat |=ENC28J60_ReadOp(ENC28J60_RBM, 0)<<8;
  /* limit retrieve len */
  if( _len > len-0x0001 )
  {
    _len = len-0x0001;
  }
  /* check CRC error */
  if( (rxstat & 0x0080) == 0 )
  {
    _len = 0; /* invalid */
  }
  else
  {
    /* copy packet from buffer */
    ENC28J60_ReadBuffer( _len, packet );
  }
  /* free read memory,update Rx pointer */
  ENC28J60_Write( ENC28J60_ERXRDPTL, (NextPacketPtr) );
  ENC28J60_Write( ENC28J60_ERXRDPTH, (NextPacketPtr)>>8 );
  /*decrement packet counter*/
  ENC28J60_WriteOp( ENC28J60_BFS, ENC28J60_ECON2, ECON2_PKTDEC );
  
  return(_len);
}